package com.pz.chat.config.netty;

import com.alibaba.fastjson2.JSON;
import com.pz.chat.constant.MessageCodeEnum;
import com.pz.chat.constant.MessageTypeEnum;
import com.pz.chat.entity.ChatMessageEntity;
import com.pz.chat.utils.RedisUtil;
import io.netty.buffer.ByteBuf;
import io.netty.channel.Channel;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelId;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.group.ChannelGroup;
import io.netty.channel.group.DefaultChannelGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.handler.codec.http.websocketx.TextWebSocketFrame;
import io.netty.util.concurrent.GlobalEventExecutor;
import lombok.Getter;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.context.annotation.Configuration;

import javax.annotation.PreDestroy;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ThreadPoolExecutor;

@Configuration
@Getter
@RefreshScope
@Slf4j
public class NettyConfig{
    @Autowired
    private RedisUtil redisUtil;

    @Value("${netty.websocket.server.name}")
    private String serverName;
    @Value("${netty.websocket.server.port}")
    private int wsPort;
    @Value("${netty.websocket.server.path}")
    private String wsPath;
    @Value("${netty.websocket.server.discoveryIP}")
    private String nettyDiscoveryIp;

    private final EventLoopGroup bossGroup = new NioEventLoopGroup(1);

    private final EventLoopGroup workerGroup = new NioEventLoopGroup(Runtime.getRuntime().availableProcessors() * 2);

    /**
     * 聊天对象映射 {key: 主用户ID， value: 聊天对象ID}
     */
    private final Map<String, String> channelTargetMap = new ConcurrentHashMap<>();

    /**
     * 传递大二进制数据的map映射 {key: 发送者ID， value:byte切片}
     */
    private final Map<String, ByteBuf> byteBufMap = new ConcurrentHashMap<>();

    /**
     * 存储接入的客户端的channel对象
     */
    private final ChannelGroup channelGroup = new DefaultChannelGroup(GlobalEventExecutor.INSTANCE);

    /**
     * 存储客户端Id和channelId的 Map 映射
     *  普通客户端 key: userId, value: channelId
     *  服务客户端 key: ip:port, value: channelId
     */
    private final Map<String, ChannelId> clientMap = new ConcurrentHashMap<>();
    public static String serverClientKey(String ip, int port) {
        return ip+":"+port;
    }

    public Channel getChannel(String userId){
        if(this.clientMap.get(userId) == null) {
            return null;
        }
        return channelGroup.find(clientMap.get(userId));
    }

    /**
     * key : channelId, value userId
     */
    private final Map<String, String> channelMap = new ConcurrentHashMap<>();

    // redis储存格式 key: ClientServerBind::{fromId} - value: ${client所在服务器ip}-${port}-${channelId}
    private static final String ClientServerBindKeyPrefix = "ClientServerBind::";

    /**
     * 清除连接记录
     */
    public void fresh(ChannelHandlerContext ctx) {
        if (ctx.channel() != null && channelGroup.find(ctx.channel().id()) != null) {
            channelGroup.remove(ctx.channel());
        }
        String channelId =  ctx.channel().id().toString();
        String userId = this.channelMap.get(channelId);
        if (userId!= null) {
            if(this.clientMap.get(userId) != null) {
                this.clientMap.remove(userId);
            }
            this.channelTargetMap.remove(userId);
            this.getByteBufMap().remove(userId);
            redisUtil.del(getClientServerBindKey(userId));
        }
        if(this.channelMap.get(channelId) != null) {
            this.channelMap.remove(channelId);
        }
    }

    public String getClientServerBindKey(String userId) {
        return ClientServerBindKeyPrefix+userId;
    }


    /**
     * 清除远程redis注册的channel信息
     */
    public void cleanRemoteChannelRegister() {
        for (String key : clientMap.keySet()) {
            redisUtil.del(getClientServerBindKey(key));
        }
    }

    /**
     * 向本机的所有客户端发送服务器下线消息
     */
    @PreDestroy
    private void releaseGroup(){
        log.info("before nettyConfig destroy...");
        //给客户端群发服务下线的通知消息
        for (String key : clientMap.keySet()) {
            ChannelId channelId = clientMap.get(key);
            Channel channel = channelGroup.find(channelId);
            ChatMessageEntity chatMessage = new ChatMessageEntity("0", key, MessageCodeEnum.SERVER_CLOSE.getMsg(), MessageTypeEnum.SERVER_INFO.getType(), MessageCodeEnum.SERVER_CLOSE.getCode(), 1);
            channel.writeAndFlush(new TextWebSocketFrame(JSON.toJSONString(chatMessage)));
            channel.closeFuture();
        }
        bossGroup.shutdownGracefully();
        workerGroup.shutdownGracefully();
    }
}
